#include "WidowsInstallerHijackUtility.h"


#ifdef HIJACKSETUP
#ifndef _DEBUG
#pragma comment(linker, "/entry:HijackSetup")
#else /* _DEBUG */
int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
	HijackSetup();
	return;
}
#endif /* _DEBUG */

#define MODULENAME_SIZE 10
static const char MODULENAME[MODULENAME_SIZE] = "setup.exe";
static const char CMD_FILENAME[] = "cmd.exe";
#define FILE1NAME_SIZE 13
static const char FILE1NAMESEQUENCE[FILE1NAME_SIZE+3] = "/c presetup.cmd";
#define FILE1NAME FILE1NAMESEQUENCE[3]
#define FILE2NAME_SIZE 13
static const char FILE2NAME[FILE2NAME_SIZE] = "setupORG.exe";
#define FILE3NAME_SIZE 13
static const char FILE3NAME[FILE3NAME_SIZE] = "setupold.exe"
#define FILE4NAME_SIZE 14
static const char FILE4NAMESEQUENCE[FILE4NAME_SIZE+3] = "/c postsetup.cmd";
#define FILE4NAME FILE3NAME[FILE3NAME_SIZE+3]

__declspec(naked) BOOL __fastcall FileExistA(PCSTR lpFileName)
{
	__asm{
		sub esp,320
		push esp
		push ecx
		call dword ptr[FindFirstFileA]
		xor ecx,ecx
		cmp eax,0xFFFFFFFF
		jz FileNotFound
		push eax
		call dword ptr[FindClose]
		xor eax,eax                   /* File Not Found */
		mov ecx,dword ptr[esp]
		and ecx,FILE_ATTRIBUTE_DIRECTORY /* ecx = File is Dir? */
		jnz FileIsDir
FileNotFound:
		inc eax               /* If file not found, return value = -1 + 1 = 0(FALSE)
							   * If file found and is not dir, return value = 0 + 1 = 1(TRUE)
							   */
FileIsDir:
		add esp,320           /* If file found and is dir, return value = 0(FALSE) */
		ret
	}
}

__declspec(naked) void HijackSetup()
{
	__asm{
		push ebp
		mov ebp,esp
		mov ecx,MAX_PATH
		sub esp,ecx
		xor ebx,ebx
		mov edi,esp
		push ecx
		push edi
		push ebx
		call dword ptr [GetModuleFileNameA]
pszModulePathInc:
		inc edi
		cmp [edi],'\\'
		jnz judgeModulePathTerminator
		inc edi
		mov esi,edi
judgeModulePathTerminator:
		cmp [edi],bl
		jnz pszModulePathInc
/*judgeModulePathFinish: */ /* esi = pszModuleName */
		mov edx,edi
		sub edx,esi
		mov edi,esi
		mov ecx,offset MODULENAME
compareModuleName:
		mov al,[ecx]
		scasb
		jz compareModuleNameNextChar
		sub al,20h            /* lower case -> upper case */
		jc ModuleNameMisMatch /* 00h-20h makes CF=1   .(2E)-20h makes 0E, which cannot be used in file name. */
		dec edi
		scasb
		jnz ModuleNameMisMatch
compareModuleNameNextChar:
		inc ecx
		test al,al
		jnz compareModuleName
/* compareModuleNameFinish: */
		push esp
		mov esi,MAX_PATH
		push esi
		call dword ptr[GetCurrentDirectoryA]
		mov edi,esp
		mov ecx,esi
		xor eax,eax
		repnz scasb
		dec edi
		dec edi
		mov al,'\\'
		scasb
		jz SkipWriteBackSlash
		stosb
SkipWriteBackSlash:            /* edi -> c:\windows\system32\HERE */
		mov ebx,edi
		mov ecx,edi
		sub ecx,esp
		mov edx,FILE1NAME_SIZE
		mov esi,esp
		sub esp,ecx
		sub esp,edx
		and esp,0FFFFFFFCh
		mov edi,esp
		push esi               /* save esi(old esp) - filename string2 */
		rep movsb
		mov eax,edi            /* save edi, we overwrite presetup.cmd with setupold.exe / setup.exe later */
		mov esi,offset FILE1NAME
		mov ecx,edx
		rep movsb

		mov esi,offset FILE2NAME
		mov ecx,FILE2NAME_SIZE
		mov edi,ebx
		rep movsb
		pop ebx                /* restore esi(old esp) to ebx - filename string2 */
		mov edi,eax            /* restore edi, to overwrite presetup.cmd with setupold.exe */
		mov ecx,esp
		call FileExistA
		test eax,eax
		jz FileNotFound1
		mov ecx,ebx
		call FileExistA
		test eax,eax
		jnz CheckFilesExistanceFinish
/* FileNotFound2: */
FileNotFound1: /* ebx = filename string2    esp = filename string1 */
		mov ebp,esp
		mov edi,ebx
		sub edi,esp
		/* BAD HABIT:
		 * Here we assume FILE1NAME_SIZE == FILE2NAME_SIZE
		 * so edi == sizeof(FILE1PATH) == cch(FILE1PATH) == cch(FILE2PATH)
		 */
		mov ecx,IDS_FILE_NOT_FOUND_PROMPT_PART1
		call GetStringAreaW
		mov esi,eax
		mov ecx,IDS_FILE_NOT_FOUND_PROMPT_PART2
		call GetStringAreaW
		movzx edx,word ptr[eax]
		movzx ecx,word ptr[esi]
		lea edx,[ecx+edx*2]
		lea edx,[edx+edi*2]
		sub esp,edx
		and esp,0FFFFFFFCh
		inc esi
		inc esi
		mov edi,esp
		push eax
		push ecx
		push esi
		push ecx
		push esi    /* save 5 regs for later use */
		mov esi,ebx
		sub esi,5
SearchFile1StrEnd:
		inc esi
		or byte ptr[esi],0
		jnz SearchFile1StrEnd
		sub esi,ebp
		/* edi - MessageBox Message Buffer
		 * esi - File path String 1 Length
		 * esp - nothing, dynamic stack pointer
		 * ebp - File path String 1
		 * ebx - File path String 2
		 */
		push esi
		push edi
		push esi
		push ebp
		push MB_PRECOMPOSED
		push CP_ACP
		call dword ptr[MultiByteToWideChar]
		shl eax,1
		add edi,eax
		mov edx,esi   /* save esi */
		pop esi       /* IDS_FILE_NOT_FOUND_PROMPT_PART1 */
		pop ecx
		rep movsw
		push edx
		push edi
		push edx
		push ebx
		push MB_PRECOMPOSED
		push CP_ACP
		call dword ptr[MultiByteToWideChar]
		shl eax,1
		add edi,eax
		pop esi
		pop ecx
		rep movsw
		pop esi
		movzx ecx,word ptr[esi]
		inc esi
		inc esi
		rep movsw
		mov ecx,IDS_FILE_NOT_FOUND_PROMPT_TITLE
		jmp MovESIESPAndGetStringAreaWAndMessageBoxAndExit
CheckFilesExistanceFinish:
/* CheckSetupOldNotExist: */
		mov esi,offset FILE3NAME
		mov ecx,FILE3NAME_SIZE
		rep movsb
		mov ecx,esp
		call FileExistA
		test eax,eax
jnz_SetupOldExist:
		jnz SetupOldExist
		test ecx,ecx
		jnz jnz_SetupOldExist
        /* eax==0 (FileExistA's return value) */
		mov edx,offset FILE1NAMESEQUENCE
		call CMDExecuteAndWait
/* CheckForPostSetupCMDExist */
		mov esi,offset FILE4NAME
		mov ecx,FILE4NAME_SIZE
		sub edi,FILE3NAME_SIZE
		rep movsb
		mov ecx,esp
		call FileExistA
		mov [ebp-4],eax
/* CheckForPostSetupCMDExistEnd -- the return value is placed at stack end */
		push MOVEFILE_WRITE_THROUGH
		push offset FILE3NAME
		push offset MODULENAME
		mov edi,dword ptr[MoveFileA]
		call edi
		mov ecx,esp            /* file string 1, which used to be presetup.cmd but now setup.exe */
		call FileExistA
		test eax,eax
		jnz MoveFail1
		push MOVEFILE_WRITE_THROUGH
		push offset MODULENAME
		push offset FILE2NAME
		call edi
		mov ecx,ebx
		call FileExistA
		test eax,eax
		jz PrepareCompleted
/* MoveFail2: */
		mov ecx,IDS_FILE_MOVE_ERROR_TYPE2
		jmp MoveFailInitFinish
MoveFail1:
		mov ecx,IDS_FILE_MOVE_ERROR_TYPE1
MoveFailInitFinish:
		call GetStringAreaW
		lea esi,[eax+1*2]
		mov ecx,IDS_FILE_MOVE_ERROR_TITLE
		jmp jmp_GetStringAreaWAndMessageBoxAndExit
PrepareCompleted:
		/* the stack is already messed up, so restore esp */
		leave
		sub esp,8 /* Get Our PostSetup.cmd Existance to stack top, followed by stored ebp */
/* LaunchOriginalSetup: */
		xor edi,edi
		mov ecx,29  /*(16+68)/4+8 */ /* (sizeof(pi) + sizeof(si))/4 + 8 CreateProcess Args */
Pushedi:
		push edi
		loop Pushedi
		add esp,8*4
		lea eax,[esp+16]
		or dword ptr[eax],68  /* si.cb=sizeof(si); */
		push esp
		push eax
		sub esp,6*4
		call dword ptr[GetCommandLine]
		push eax
		push edi
		call dword ptr[CreateProcess]
		test eax,eax
		jz CreateSetupEXEProcessFail
		mov edi,dword ptr[esp]
		push INFINITE
		push edi
		call dword ptr[WaitForSingleObject]
		lea edx,[esp+16+68-4+8] /* original esp - 4 (+8 = setupold.cmd exist & stored ebp) */
		push edx
		push edi
		call dword ptr[GetExitCodeProcess]
		mov edi,dword ptr[CloseHandle]
		call edi
		call edi
		add esp,16+68-8 /* -8: last 2 calls, now stack has 2 elements: setupold.cmd exist & Exitcode */
		pop ecx
		jecxz SkipPostSetupCMD /* jmp if Postsetup.cmd not exists */
		xor eax,eax
		mov edx,offset FILE4NAMESEQUENCE
		call CMDExecuteAndWait
SkipPostSetupCMD:
		pop ebp
		call WihuPostSetup_ASMEntry
CreateSetupEXEProcessFail:
		mov ecx,IDS_SETUP_START_ERROR_MSG
		call GetStringAreaW
		lea esi,[eax+2]
		mov ecx,IDS_SETUP_START_ERROR_TITLE
jmp_GetStringAreaWAndMessageBoxAndExit:
		jmp GetStringAreaWAndMessageBoxAndExit
SetupOldExist: /* esp - pszPath   ebx - pszPath2 */
		mov edi,ebx
		sub edi,5
SearchOldFileStrEnd:
		inc edi
		or byte ptr[edi],0
		jnz SearchOldFileStrEnd
		sub edi,esp
		mov esi,esp
		mov ecx,IDS_FILE_OLD_EXIST_PROMPT_PART2
		call GetStringAreaW
		mov ebx,eax
		mov ecx,IDS_FILE_OLD_EXIST_PROMPT_PART1
		call GetStringAreaW
		mov edx,edi             /* edx = Length */
		movzx ecx,word ptr[ebx]
		add edi,ecx
		movzx ecx,word ptr[eax] /* ecx = sizeof(PART1) */
		add edi,ecx
		shl edi,1
		sub esp,edi
		and esp,0FFFFFFFCh      /* align to dword */
		mov edi,esp
		push IDS_FILE_OLD_EXIST_PROMPT_TITLE
		jmp AssembleMessageBoxConstVarConst
ModuleNameMisMatch: /* esi = pszModuleName */ /* edx = ModuleNameLength */
		mov edi,edx             /* Save edx */
		mov ecx,IDS_WRONG_NAME_PROMPT_PART2
		call GetStringAreaW
		mov ebx,eax
		mov ecx,IDS_WRONG_NAME_PROMPT_PART1
		call GetStringAreaW
		mov edx,edi             /* edx = ModuleNameLength */
		movzx ecx,word ptr[ebx]
		add edi,ecx
		movzx ecx,word ptr[eax] /* ecx = sizeof(IDS_WRONG_NAME_PROMPT_PART1) */
		add edi,ecx
		shl edi,1
		sub esp,edi
		and esp,0FFFFFFFCh      /* align to dword */
		mov edi,esp
		push IDS_WRONG_NAME_PROMPT_TITLE
AssembleMessageBoxConstVarConst:
		xchg eax,esi            /* swap eax & esi */
		                        /* esi = pAreaIDS_WRONG_NAME_PROMPT_PART1 */
		                        /* eax = pszModuleName */
		inc esi
		inc esi                 /* esi = pszIDS_WRONG_NAME_PROMPT_PART1 */
		rep movsw
		push edx
		push edi
		push edx
		push eax
		push MB_PRECOMPOSED
		push CP_ACP
		call dword ptr[MultiByteToWideChar]
		shl eax,1
		add edi,eax
		lea esi,[ebx+2]
		movzx ecx,word ptr[ebx]
		rep movsw
		pop ecx
MovESIESPAndGetStringAreaWAndMessageBoxAndExit:
		mov esi,esp
GetStringAreaWAndMessageBoxAndExit:
		call GetStringAreaW
		inc eax
		inc eax
		push ecx                /* ExitProcess - 0 */
		push ecx                /* MessageBoxW - uType=0 */
		push eax                /* MessageBoxW - lpCaption */
		push esi                /* MessageBoxW - lpMessage */
		push ecx                /* MessageBoxW - hWnd */
		call dword ptr[MessageBoxW]
		call dword ptr[ExitProcess]
CMDExecuteAndWait:
		mov ecx,9
		push INFINITE
Pusheax:
		push eax
		loop Pusheax
		push edx
		push offset CMD_FILENAME
		push eax
		push eax
		push SEE_MASK_NOCLOSEPROCESS
		push 60
		push esp
		call dword ptr[ShellExecuteExA]
		add esp,56
		call dword ptr[WaitForSingleObject]
		ret
	}
}
#endif /* HIJACKSETUP */